<?php
class CV_Datastore{
	/**
	 * 
	 * @param unknown_type $storage
	 * @return CV_Datastore
	 */
	public function create( $storage ){
		return new self($storage);
	}
	public function __construct( $pdo ){
		$this->pdo = $pdo;
		//$this->metadata_pool = ;
	}
	function save( $object, $class = NULL ){
		$record = $this->_get_object_data( $object );
		$table = $this->object_schema[ $this->_get_object_class($object) ]->table;
		return $this->save_record( $record, $table );
	}
	function save_record( $record, $table ){
		if( is_string($table) ){
			$table = $this->object_schema[$table]->table;
		}
		$has_primary_key = false;
		foreach( $table->get_primary_key() as $field ){
			if( isset($record[$field]) ) {
				$has_primary_key = true;
			} elseif( !isset($record[$field]) && $has_primary_key ){
				throw new CV_Exception( 'record has incomplete primary key' );
			}
		}
		if( $has_primary_key ){
			return $this->update_record( $record, $table );
		} else {
			return $this->insert_record( $record, $table );
		}		
	}
	function update_record( $record, $table ){
		if( is_string($table) ){
			$table = $this->object_schema[$table]->table;
		}
		$fields = array_intersect( array_keys($record), $table->columns->attribute('name')->getArrayCopy() );
		$primary_key = $table->get_primary_key();
		$primary_key_fields = array_intersect_key( $record, array_flip($primary_key) );
		$sql = 'UPDATE '.$table->sql_identifier
			.' SET '.
				implode(
					', ',
					array_map(
						create_function('$x','return "$x = ?";'),
						$fields
					)
				)
			.' WHERE '.
				implode(
					' AND ',
					array_map(
						create_function('$x','return "$x = ?";'),
						$primary_key
					)
				);
		$stmt = $this->pdo->prepare( $sql );
		$stmt->execute(
			array(
				array_values(
					array_intersect_key(
						$record,
						array_flip($fields)
					)
				), 
				array_values(
					$primary_key_fields
				)
			)
		);
	}
	function insert_record( $record, $table ){
		if( is_string($table) ){
			$table = $this->object_schema[$table]->table;
		}
		$fields = array_intersect( array_keys($record), $table->columns->attribute('name')->getArrayCopy() );
		$stmt = $this->pdo->prepare(
			'INSERT INTO '.$table->sql_identifier
			.' ('. implode(',',$fields) .')'
			.' VALUES ('.$this->pdo->markers($fields).')'
		);
		$stmt->execute(
			array_intersect_key(
				$record,
				array_flip($fields)
			)
		);
	}
	function find( $class, $where = "", $args = array() ){
		$table = $this->object_schema[ $class ]->table;
		return CV_Array::object_wrap( 
			$this->pdo->fetchAll(
				'SELECT * FROM '.$table->sql_identifier.
					( !empty($where)?
						' WHERE '.$where
					:'')
				, $args
			), $class
		);
	}
	function find_one( $class, $where = "", $args = array() ){
		$table = $this->object_schema[ $class ]->table;
		return new $class (
			$this->pdo->fetch(
				'SELECT * FROM '.$table->sql_identifier.
					( !empty($where)?
						' WHERE '.$where
					:'')
				, $args
			)
		);
	}
	function kickstart( $database ){
		$relational_metadata_manager = new CV_Datastore_Metadata_Relational_Manager();
		$model_metadata_manager      = new CV_Datastore_Metadata_Model_Manager();
		
		$relational_schema   = $relational_metadata_manager->create_provider( $this->pdo );
		$relational_schema->fetch( $database );
		$this->object_schema = $model_metadata_manager->create_provider( $relational_schema );
	}
	function create_builder( $object_or_class, $output = 'Form_Yaml' ){
		$builder_class = 'CV_Datastore_Builder_'.$output;
		
		if( is_string($object_or_class) ){
			$class = $this->object_schema[ $object_or_class ];
			return new $builder_class( $class );
		} else {
			$class = $this->object_schema[ $this->_get_object_class($object_or_class) ];
			$current_values = $this->_get_object_data($object_or_class);
			return new $builder_class( $class, $current_values );
		}
	}
	
	function _create_object( $data ){
		
	}
	function _get_object_class( $object ){
		return get_class($object);
	}
	function _get_object_data( $object ){
		return $object->getArrayCopy();
	}
}
